﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DynORM.DAL.Interfaces;
using DynORM.Enums;
using DynORM.DAL;
using DynORM.Interfaces;
using DynORM.ReferenceObjects;

namespace DynORM.Mappers
{
    public static class DatabaseMapper
    {
        public static IDAL ChooseDAL(DBMSTypes rdbmsKind)
        {
            IDAL dal = null;
            if (rdbmsKind == DBMSTypes.MSSQL)
                dal = new DAL_MSSQL();
            else if (rdbmsKind == DBMSTypes.PostgreSQL)
                dal = new DAL_PostgreSQL();
            else if (rdbmsKind == DBMSTypes.MySQL)
                dal = new DAL_MySQL();
            else if (rdbmsKind == DBMSTypes.Oracle)
                dal = new DAL_Oracle();
            else
                throw new Exception("RDBMS type is not supported.");
            return dal;
        }

        private static string ChangeDBInConnectionString(string connectionString, DBMSTypes rdbmsKind, string databaseName)
        {
            IDAL dal = ChooseDAL(rdbmsKind);
            return dal.ChangeDBInConnectionString(connectionString, databaseName);
        }

        public static void CreateDatabase(string connectionString, DBMSTypes rdbmsType, IORMDatabase db)
        {
            CreateDatabase(connectionString, rdbmsType, db, true, true);
        }

        public static void CreateDatabase(string connectionString, DBMSTypes rdbmsType, IORMDatabase db, bool createDB, bool fillTables)
        {
            if (createDB)
                CreateDB(connectionString, rdbmsType, db.DBName, db.AuxDatas.ToArray<string>());

            string connectionStringToDataBase = ChangeDBInConnectionString(connectionString, rdbmsType, db.DBName);
            CreateTables(connectionStringToDataBase, rdbmsType, db.Tables);

            if (fillTables)
                FillTables(connectionStringToDataBase, rdbmsType, db.Rows);
        }

        public static IORMDatabase RetrieveDatabase(string connectionString, DBMSTypes rdbmsType, string dbName)
        {
            IDAL dal = ChooseDAL(rdbmsType);

            string connectionStringToDataBase = ChangeDBInConnectionString(connectionString, rdbmsType, dbName);
            if (dal.TryConnect(connectionStringToDataBase) == false)
                return null; //database doesn't exist

            Database result = new Database(dbName);

            foreach (IRowDescriptor rd in dal.GetAllTablesStructure(connectionStringToDataBase))
                result.AddTable(rd);
            foreach (string tn in dal.GetAllTablesName(connectionStringToDataBase))
                foreach (IRow r in dal.SelectRows(connectionStringToDataBase, tn))
                    result.AddRow(r);

            return result;
        }

        public static void DropDatabase(string connectionString, DBMSTypes rdbmsType, string dbName, params string[] auxDatas)
        {
            IDAL dal = ChooseDAL(rdbmsType);
            dal.DropDB(connectionString, dbName, auxDatas);
        }

        private static void CreateDB(string connectionString, DBMSTypes rdbmsType, string dbName, params string[] auxData)
        {
            IDAL dal = ChooseDAL(rdbmsType);
            dal.CreateDB(connectionString, dbName, auxData);
        }

        private static void CreateTables(string connectionString, DBMSTypes rdbmsType, IEnumerable<IRowDescriptor> tables)
        {
            foreach (IRowDescriptor rd in tables)
                RowDescriptorMapper.Store(connectionString, rdbmsType, rd);
        }

        private static void FillTables(string connectionString, DBMSTypes rdbmsType, IEnumerable<IRow> rows)
        {
            foreach (IRow r in rows)
                if (RowMapper.RowRead(connectionString, rdbmsType, r.PrimaryKey) == null)
                    RowMapper.RowWrite(connectionString, rdbmsType, r);
        }

    }
}
